﻿/*
 * Catalyst - http://catalystobjects.com
 * 
 * New BSD License (BSD)
 * 
 * Copyright (c) 2009, Ashley Brener
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of Midlet Earth nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Data;
using SubSonic;

namespace Catalyst
{
	[Serializable]
	public abstract class DataObjectList<ItemType, ListType> : AbstractObjectList<ItemType, ListType>
		where ItemType : DataObject<ItemType>, new()
		where ListType : DataObjectList<ItemType, ListType>, new()
	{
		#region *** Load Data ***

		//public ListType Load<T, LT>(LT SubSonicList)
		//    where T : RecordBase<T>, new()
		//    where LT : AbstractList<T, LT>, new()
		//{
		//    ItemType dataObject = null;

		//    foreach (RecordBaseType record in SubSonicList)
		//    {
		//        dataObject = new ItemType();
		//        dataObject.Load<RecordBaseType>(record);
		//        Add(dataObject);
		//    }

		//    return (ListType)this;
		//}

		public ListType Load(IEnumerable<ItemType> collection)
		{
			foreach (ItemType item in collection)
			{
				this.Add(item);
			}

			return (ListType)this;
		}
		
		public override ListType Load(IDataReader Reader)
		{
			return Load(GetTableSchema(), Reader);
		}

		public override ListType Load<T>(IDataReader Reader)
		{
			return Load<T>(GetTableSchema(), Reader);
		}

		public override ListType Load(TableSchema.Table ViewSchema, IDataReader Reader)
		{
			return Load<ItemType>(ViewSchema, Reader);
		}

		public ListType Load<T>(TableSchema.Table ViewSchema, IDataReader Reader) where T : ItemType, new()
		{
			try
			{
				T businessObject = null;

				//loadSchema(ViewName);

				Schema = ViewSchema;

				Dictionary<string, object> fields = null;

				while (Reader.Read())
				{
					try
					{
						fields = new Dictionary<string, object>();

						for (int i = 0; i < Reader.FieldCount; i++)
						{
							fields.Add(Reader.GetName(i), Reader[i]);
						}

						businessObject = new T();
						businessObject.Load(fields);
						Add(businessObject);
					}
					catch
					{
						throw new Exception(Reader.GetName(0));
					}
				}
			}
			finally
			{
				if (Reader != null)
				{
					Reader.Close();
				}
			}

			isLoaded = true;

			return (ListType)this;
		}

		public override ListType Load(DataTable DataTable)
		{
			return Load(GetTableSchema(), DataTable);
		}

		public override ListType Load(TableSchema.Table ViewSchema, DataTable DataTable)
		{
			ItemType businessObject = null;

			//loadSchema(ViewName);
			Schema = ViewSchema;

			foreach (DataRow row in DataTable.Rows)
			{
				businessObject = new ItemType();
				businessObject.Load(row);
				Add(businessObject);
			}

			isLoaded = true;

			return (ListType)this;
		}

		/// <summary>
		/// Returns BusinessObjectList as foreign from a cache
		/// </summary>
		/// <param name="ForeignKeyName"></param>
		/// <param name="PrimaryKeyValue"></param>
		/// <returns></returns>
		public ListType LoadAsForeign(string ForeignKeyName, object PrimaryKeyValue)
		{
			ListType abstractObjects = new ListType();
			abstractObjects.LoadAll();

			foreach (ItemType item in abstractObjects)
			{
				if (item.GetDataValue(ForeignKeyName).Equals(PrimaryKeyValue))
				{
					Add(item);
				}
			}

			return (ListType)this;
		}

		#endregion

		#region *** Relations ***

		public void LoadRelations()
		{
			if (items != null && items.Count > 0)
			{
				foreach (ItemType businessObject in items)
				{
					businessObject.LoadRelations();
				}
			}
		}

		public void LoadRelations(IList<string> RelationNames)
		{
			if (items != null && items.Count > 0 && RelationNames != null && RelationNames.Count > 0)
			{
				relationNamesToLoad = new List<string>(RelationNames);

				foreach (ItemType businessObject in items)
				{
					businessObject.LoadRelations(relationNamesToLoad);
				}
			}
		}

		#endregion

		public void Sort(string DataColumnName, bool Ascending)
		{
			if (!String.IsNullOrEmpty(DataColumnName))
			{
				ListComparer<ItemType> compare = new ListComparer<ItemType>();
				ItemType item = new ItemType();
				DbType dbType = item.GetDBType(DataColumnName);
				compare.Ascending = Ascending;
				compare.DataColumnName = DataColumnName;
				compare.DBType = dbType;

				Sort(compare);
			}
		}
	}

	[Serializable]
	class ListComparer<ItemType> : Comparer<ItemType> where ItemType : DataObject<ItemType>, new()
	{
		private string dataColumnName;

		public string DataColumnName
		{
			get { return dataColumnName; }
			set { dataColumnName = value; }
		}
		private bool ascending;

		public bool Ascending
		{
			get { return ascending; }
			set { ascending = value; }
		}

		private DbType dbType = DbType.String;

		public DbType DBType
		{
			get { return dbType; }
			set { dbType = value; }
		}

		public override int Compare(ItemType x, ItemType y)
		{
			object xVal = x.GetDataValue<object>(dataColumnName);
			object yVal = y.GetDataValue<object>(dataColumnName);
			int result;

			if (dbType == DbType.String || dbType == DbType.Guid)
			{
				string sX = xVal.ToString();
				string sY = yVal.ToString();
				result = sX.CompareTo(sY);
			}
			else if (dbType == DbType.DateTime)
			{
				DateTime dX = Convert.ToDateTime(xVal);
				DateTime dY = Convert.ToDateTime(yVal);
				result = dX.CompareTo(dY);
			}
			else if (dbType == DbType.Boolean)
			{
				bool bX = Convert.ToBoolean(xVal);
				bool bY = Convert.ToBoolean(yVal);
				result = bX.CompareTo(bY);
			}
			else
			{
				double dX = Convert.ToDouble(xVal);
				double dY = Convert.ToDouble(yVal);
				result = dX.CompareTo(dY);
			}

			if (!ascending)
			{
				result *= -1;
			}
			return result;
		}
	}
}